/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package models;

import utilities.DrawingBean;
import java.awt.Point;
import java.util.Random;
import java.util.Vector;
import simulation.GUIOptionManager;

/**
 * Includes deployment and movement algorithms
 * @author Arvanitis Ioannis
 */
public final class Mobility {



    /**
     * Random Waypoint Model
     * @param initial Initial position of node
     * @return a new position for the initial point
     */
    public static Point getRandomWaypoint(Point initial) {
        boolean isMoved = false;
        Random generator = new Random();
        Point newpos = null;
        while (!isMoved) {
            newpos = new Point(initial);
            int oriented = (int) Math.floor(5 * generator.nextDouble());
            int velocity = (int) Math.ceil(GUIOptionManager.getMaxVelocity() * generator.nextDouble());
            switch (oriented) {
                case 0: //STABLE
                    isMoved = true;
                    break;
                case 1: //NORTH
                    if (newpos.y - velocity >= 0) {
                        newpos.y -= velocity; isMoved = true;
                    }
                    break; 
                case 2: //EAST
                    if (newpos.x + velocity <= GUIOptionManager.getMapWidthMeters()) {
                        newpos.x += velocity; isMoved = true;
                    }
                    break;
                case 3: //SOUTH
                    if (newpos.y + velocity <= GUIOptionManager.getMapHeightMeters()) {
                        newpos.y += velocity; isMoved = true;
                    }
                    break;
                case 4: //WEST
                    if (newpos.x - velocity >= 0) {
                        newpos.x -= velocity; isMoved = true;
                    }
                    break;
            }
            if (isMoved && oriented != 0 && !isFreePosition(newpos)) {
                isMoved = false;
            }
        }
        return newpos;
    }

    /**
     * Reference Point Group Mobility Model
     * @param initial Initial position of node
     * @return a new position for the initial point
     */
    public static Point getReferencePointGroupMobility(Point initial, int groupOrientation, int groupVelocity) {
        if (groupOrientation == 0) {
            return getRandomWaypoint(initial);
        }
        else {
            boolean isMoved = false;
            Random generator = new Random();
            Point newpos = null;
            while (!isMoved) {
                newpos = new Point(initial);
                int oriented = (int) Math.floor(5 * generator.nextDouble());
                int velocity = (int) Math.ceil(GUIOptionManager.getMaxVelocity() * generator.nextDouble());
                if (oriented == 0) {
                    switch (groupOrientation) {
                        case 1: //NORTH
                            if (newpos.y - groupVelocity >= 0) {
                                newpos.y -= groupVelocity; isMoved = true;
                            }
                            break;
                        case 2: //EAST
                            if (newpos.x + groupVelocity <= GUIOptionManager.getMapWidthMeters()) {
                                newpos.x += groupVelocity; isMoved = true;
                            }
                            break;
                        case 3: //SOUTH
                            if (newpos.y + groupVelocity <= GUIOptionManager.getMapHeightMeters()) {
                                newpos.y += groupVelocity; isMoved = true;
                            }
                            break;
                        case 4: //WEST
                            if (newpos.x - groupVelocity >= 0) {
                                newpos.x -= groupVelocity; isMoved = true;
                            }
                            break;
                    }
                    if (isMoved && !isFreePosition(newpos)) {
                        isMoved = false;
                    }   
                }
                else {
                    boolean sameDirection = (Math.abs(groupOrientation - oriented) % 2 == 0 ) ? true : false ;
                    if (sameDirection) {
                        velocity = ((groupOrientation == oriented) ? velocity+groupVelocity : velocity-groupVelocity) ;
                        if (velocity < 0) {
                            velocity = Math.abs(velocity);
                            oriented = groupOrientation;
                        }
                        else if (velocity == 0) {
                            oriented = 0;
                        }
                        switch (oriented) {
                            case 0: //STABLE
                                isMoved = true;
                                break;
                            case 1: //NORTH
                                if (newpos.y - velocity >= 0) {
                                    newpos.y -= velocity; isMoved = true;
                                }
                                break;
                            case 2: //EAST
                                if (newpos.x + velocity <= GUIOptionManager.getMapWidthMeters()) {
                                    newpos.x += velocity; isMoved = true;
                                }
                                break;
                            case 3: //SOUTH
                                if (newpos.y + velocity <= GUIOptionManager.getMapHeightMeters()) {
                                    newpos.y += velocity; isMoved = true;
                                }
                                break;
                            case 4: //WEST
                                if (newpos.x - velocity >= 0) {
                                    newpos.x -= velocity; isMoved = true;
                                }
                                break;
                        }
                        if (isMoved && oriented != 0 && !isFreePosition(newpos)) {
                            isMoved = false;
                        }
                    }
                    else {
                        switch (oriented) {
                            case 0: //STABLE
                                isMoved = true;
                                break;
                            case 1: //NORTH
                                if (newpos.y - velocity >= 0) {
                                    newpos.y -= velocity; isMoved = true;
                                }
                                break;
                            case 2: //EAST
                                if (newpos.x + velocity <= GUIOptionManager.getMapWidthMeters()) {
                                    newpos.x += velocity; isMoved = true;
                                }
                                break;
                            case 3: //SOUTH
                                if (newpos.y + velocity <= GUIOptionManager.getMapHeightMeters()) {
                                    newpos.y += velocity; isMoved = true;
                                }
                                break;
                            case 4: //WEST
                                if (newpos.x - velocity >= 0) {
                                    newpos.x -= velocity; isMoved = true;
                                }
                                break;
                        }
                        if (isMoved) {
                            switch (groupOrientation) {
                                case 1: //NORTH
                                    if (newpos.y - groupVelocity >= 0) {
                                        newpos.y -= groupVelocity; isMoved = true;
                                    }
                                    else {
                                        isMoved = false;
                                    }
                                    break;
                                case 2: //EAST
                                    if (newpos.x + groupVelocity <= GUIOptionManager.getMapWidthMeters()) {
                                        newpos.x += groupVelocity; isMoved = true;
                                    }
                                    else {
                                        isMoved = false;
                                    }
                                    break;
                                case 3: //SOUTH
                                    if (newpos.y + groupVelocity <= GUIOptionManager.getMapHeightMeters()) {
                                        newpos.y += groupVelocity; isMoved = true;
                                    }
                                    else {
                                        isMoved = false;
                                    }
                                    break;
                                case 4: //WEST
                                    if (newpos.x - groupVelocity >= 0) {
                                        newpos.x -= groupVelocity; isMoved = true;
                                    }
                                    else {
                                        isMoved = false;
                                    }
                                    break;
                            }
                        }
                    } 
                }   
            }
            return newpos;
        }   
    }

    /**
     * Determines whether a given position is free
     * @param p Point that refers to the specified position to be checked
     * @return Whether or not a specific position is occupied
     */
    private static boolean isFreePosition(Point p) {
        if (isOccupied(p, GUIOptionManager.getObstacles(), GUIOptionManager.getNodes())) {
            return false;
        }
        else {
            return true;
        }
    }

    /**
     * Examines whether the specified position is occupied/blocked or free to place a node
     * @param p Point under examination
     * @param obstacles_v Vector with obstacles (in grid)
     * @param nodes_v Vector with nodes' position details
     * @return whether the specified position is occupied/blocked or free to place a node
     */
    private static boolean isOccupied (Point p, Vector<DrawingBean> obstacles_v, Vector<DrawingBean> nodes_v) {
        if (obstacles_v != null) {
            for (int i=0; i<obstacles_v.size(); i++) {
                if (obstacles_v.get(i).getPoint().x * GUIOptionManager.getMapWidthMeters() / GUIOptionManager.getMapWidthObstacles() <= p.x
                        && (obstacles_v.get(i).getPoint().x+1) * GUIOptionManager.getMapWidthMeters() / GUIOptionManager.getMapWidthObstacles() >= p.x
                        && obstacles_v.get(i).getPoint().y * GUIOptionManager.getMapHeightMeters() / GUIOptionManager.getMapHeightObstacles() <= p.y
                        && (obstacles_v.get(i).getPoint().y+1) * GUIOptionManager.getMapHeightMeters() / GUIOptionManager.getMapHeightObstacles() >= p.y) {
                    return true;
                }
            }
        }
        if (nodes_v != null) {
            for (int i=0; i<nodes_v.size(); i++) {
                if (nodes_v.get(i).getPoint().x == p.x && nodes_v.get(i).getPoint().y == p.y) {
                    return true;
                }
            }
        }
        return false;
    }




}
